#include "windows.h"
//#include <time.h>
#include "linklist.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "tchar.h"

LPLISTHEADER _GetListHeader(HLIST);
BOOL _FreeListHeader(HLIST);
void _ListSort(int, DWORD, LPINT, LPLISTSORTINFO);
int _ListCompareKeys(LPVOID, LPVOID, LPLISTSORTINFO);
long _SwapDate(LPTSTR);

HLIST WINAPI ListCreate()
{
	HGLOBAL hList;
	LPLISTHEADER lpList;
	
	hList = GlobalAlloc(GHND, sizeof(LISTHEADER));                                                
	if (hList == NULL)
		return hList;
	lpList = (LPLISTHEADER)GlobalLock(hList);
	if (lpList == NULL)
		return NULL;
	lpList->lpFirstItem = NULL;
	lpList->lpLastItem = NULL;
	lpList->iNumItems = 0;
	lpList->SortInfo.Key[0].Type = -1;
	return hList;
}

BOOL WINAPI ListDestroy(HLIST hList)
{
	if (hList == NULL) return TRUE;
	if (!ListDelete(hList))
		return FALSE;
	return (GlobalFree(hList) == NULL);
}	

void FAR * WINAPI ListMalloc(size_t Size)
{
	void FAR *ptr;
	ptr = MALLOC(Size);
	MEMSET(ptr, 0, Size); //initialize buffer to zeros
	return ptr;
}

void WINAPI ListFree(void FAR *lpBlock)
{
	FREE(lpBlock);
}

BOOL WINAPI ListAddTail(HLIST hList, void FAR *lpItem)
{
	LPLISTHEADER lpList;	
	LPLISTITEM lpNew;
	LPLISTITEM lpLast = NULL;  
	
	lpList = _GetListHeader(hList);
	if (lpList->lpLastItem != NULL)
	{                        
		lpLast = lpList->lpLastItem;
	}
	
	lpNew = (LPLISTITEM)ListMalloc(sizeof(LISTITEM));

	if (lpLast != NULL)                     
	{
		lpLast->lpNextItem = lpNew;		
	}

	lpNew->lpPrevItem = lpList->lpLastItem;
	lpNew->lpNextItem = NULL;    
	lpNew->lpItem = lpItem;
	lpList->lpLastItem = lpNew;
	if (lpList->lpFirstItem == NULL)
		lpList->lpFirstItem = lpNew;
	lpList->iNumItems++;
	_FreeListHeader(hList);
	return TRUE;
}

BOOL WINAPI ListDelete(HLIST hList)
{               
	LPLISTHEADER lpList;	
	LPLISTITEM lpNext;
	LPLISTITEM lpItem;
	
	if (hList == NULL)
		return TRUE;
	
	lpNext = ListGetHead(hList);
	while (lpNext != NULL)
	{
		lpItem = lpNext;
		ListFree(lpItem->lpItem);
		lpNext = lpItem->lpNextItem;
		ListFree(lpItem);
	}
	lpList = _GetListHeader(hList);
	lpList->lpFirstItem = NULL;
	lpList->lpLastItem = NULL; 
	lpList->iNumItems = 0; 
	HEAPMIN();
	_FreeListHeader(hList);
	return TRUE;
}

LPLISTITEM WINAPI ListGetHead(HLIST hList)
{
	LPLISTHEADER lpList;	
	LPLISTITEM lpItem;

	lpList = _GetListHeader(hList);
	lpItem = lpList->lpFirstItem;
	_FreeListHeader(hList);
	return lpItem;
} 

LPLISTITEM WINAPI ListGetTail(HLIST hList)
{
	LPLISTHEADER lpList;	
	LPLISTITEM lpItem;

	lpList = _GetListHeader(hList);
	lpItem = lpList->lpLastItem;
	_FreeListHeader(hList);
	return lpItem;
}                

int WINAPI ListGetSortedBy(HLIST hList)
{
	LPLISTHEADER lpList;	
	int iRet;
	
	if (hList == NULL)
		return -1;
	lpList = _GetListHeader(hList);
	iRet = lpList->SortInfo.Key[0].Type;
	_FreeListHeader(hList);
	return iRet;
}

BOOL WINAPI ListSortedAscending(HLIST hList)
{
	LPLISTHEADER lpList;	
	BOOL bRet;

	lpList = _GetListHeader(hList);
	bRet = lpList->SortInfo.Ascending;
	_FreeListHeader(hList);
	return bRet;
}

void WINAPI ListGetSortInfo(HLIST hList, LPLISTSORTINFO lpSortInfo)
{
	LPLISTHEADER lpList;	                              
	
	lpList = _GetListHeader(hList);
	*lpSortInfo = lpList->SortInfo;
	_FreeListHeader(hList);
}

void FAR * WINAPI ListGetNext(HLIST hList, LPLISTITEM FAR *plpItem)
{
	void FAR *lpItem;
	
	lpItem = (*plpItem)->lpItem;
	*plpItem = (LPLISTITEM)(*plpItem)->lpNextItem;
	return lpItem;
}

void FAR * WINAPI ListGetPrev(HLIST hList, LPLISTITEM FAR *plpItem)
{
	void FAR *lpItem;
	
	lpItem = (*plpItem)->lpItem;
	*plpItem = (LPLISTITEM)(*plpItem)->lpPrevItem;
	return lpItem;
}

void WINAPI ListDeleteItem(HLIST hList, LPLISTITEM lpItem)
{
	LPLISTHEADER lpList;	            
	LPLISTITEM lpi;

	lpList = _GetListHeader(hList);

	//make next item point to prev item
	if (lpItem->lpNextItem != NULL)
	{
		lpi = (LPLISTITEM)lpItem->lpNextItem;
		lpi->lpPrevItem = lpItem->lpPrevItem;
	}                                                
	else
	{ //item being deleted is last in list
		lpList->lpLastItem = lpItem->lpPrevItem;
	}
		
	//make prev item point to next item
	if (lpItem->lpPrevItem != NULL)
	{
		lpi = (LPLISTITEM)lpItem->lpPrevItem;
		lpi->lpNextItem = lpItem->lpNextItem;
	}
	else
	{ //item being deleted is first in list
		lpList->lpFirstItem = lpItem->lpNextItem;
	}    
	ListFree(lpItem->lpItem);                                 
	ListFree(lpItem);                                 
	lpList->iNumItems--;
	_FreeListHeader(hList);
}

void WINAPI ListRemoveItem(HLIST hList, LPLISTITEM lpItem)
{
	LPLISTHEADER lpList;	            
	LPLISTITEM lpi;

	lpList = _GetListHeader(hList);

	//make next item point to prev item
	if (lpItem->lpNextItem != NULL)
	{
		lpi = (LPLISTITEM)lpItem->lpNextItem;
		lpi->lpPrevItem = lpItem->lpPrevItem;
	}                                                
	else
	{ //item being deleted is last in list
		lpList->lpLastItem = lpItem->lpPrevItem;
	}
		
	//make prev item point to next item
	if (lpItem->lpPrevItem != NULL)
	{
		lpi = (LPLISTITEM)lpItem->lpPrevItem;
		lpi->lpNextItem = lpItem->lpNextItem;
	}
	else
	{ //item being deleted is first in list
		lpList->lpFirstItem = lpItem->lpNextItem;
	}    
	ListFree(lpItem);                                 
	lpList->iNumItems--;
	_FreeListHeader(hList);
}

int WINAPI ListGetNumItems(HLIST hList)
{
	LPLISTHEADER lpList;	
	int i;

	if (hList == NULL)
		return 0;
	lpList = _GetListHeader(hList);
	i = lpList->iNumItems;
	_FreeListHeader(hList);
	return i;
}	

LPLISTHEADER _GetListHeader(HLIST hList)
{
	return (LISTHEADER far *)GlobalLock(hList);
}

BOOL _FreeListHeader(HLIST hList)
{
	return GlobalUnlock(hList);
}

BOOL WINAPI ListSort(HLIST hList, LPLISTSORTINFO pSortInfo)
{ 
	HGLOBAL hInArr, hOutArr;
	LPLISTHEADER lpList;	
	DWORD dwInArr;   
	DWORD FAR *lpInArr;
	LPINT lpOutArr;
	LPLISTITEM lpItem;   
	LPLISTITEM lpNext;
	int i = 0;
	                           
	lpList = _GetListHeader(hList);
	
	if (lpList->iNumItems < 2)
	{
		_FreeListHeader(hList);
		return TRUE;
	}		

//allocate memory for array of far pointers
	hInArr = GlobalAlloc(GHND, (lpList->iNumItems + 2) * sizeof(DWORD));
	if (hInArr == NULL)
		return FALSE;	
//	lpInArr = (DWORD)GlobalLock(hInArr);
	dwInArr = (DWORD)GlobalLock(hInArr);
	if (dwInArr == 0L)
		return FALSE;             
	lpInArr = (DWORD FAR *)dwInArr;
	
	//allocate memory for sorted array
  hOutArr = GlobalAlloc(GHND, (DWORD)((lpList->iNumItems + 2) * sizeof(int)));
  if (hOutArr == NULL) 
  {
    return FALSE;
  }
  lpOutArr = (LPINT)GlobalLock(hOutArr);
  if (lpOutArr == NULL)
  	return FALSE;

	//load handles of list items into array
	lpNext = ListGetHead(hList);
	while (lpNext != NULL)
	{
		lpInArr[i++] = (DWORD)ListGetNext(hList, (LPLISTITEM FAR *)&lpNext);
	}                

	//do sort                               
	_ListSort(lpList->iNumItems, dwInArr, lpOutArr, pSortInfo);
	
	//we now have an array of far pointers in correct order, so
	//need to juggle list items about
	
	lpNext = ListGetHead(hList);
	i = 0;
	while (lpNext != NULL)
	{
		lpItem = lpNext;
		lpItem->lpItem = (LPVOID)lpInArr[lpOutArr[i]];		
		lpNext = lpItem->lpNextItem;
		i++;
	}

//	lpList->eSortedBy = pSortInfo->Key[0].Type;
//	lpList->bAscending = pSortInfo->Ascending;
	lpList->SortInfo = *pSortInfo;
	
	//All done!!
	//cleanup
	GlobalUnlock(hInArr);
	GlobalUnlock(hOutArr);
	GlobalFree(hInArr);
	GlobalFree(hOutArr);
	_FreeListHeader(hList);
	return TRUE;
}
	

//sort routine nicked from general container (gcsort)
void _ListSort(int NumSort, DWORD dwInArr, LPINT pOutArr, LPLISTSORTINFO pSortInfo)
{      
  int   i,s,t,p,q;
  HGLOBAL hMem;
  LPINT  pSort;
	DWORD FAR *lpInArr;

	lpInArr = (DWORD FAR *)dwInArr;
  if ( (hMem = GlobalAlloc(GHND, (DWORD)((NumSort+2) * sizeof(int)))) == NULL ) 
  {
    return;
  }
  pSort = (LPINT)GlobalLock(hMem);
  if (pSort == NULL)
  {
		GlobalFree(hMem);
  	return;
  }

  #define sign(x,y)  ( (x>=0)? abs(y) : -abs(y) )

  LONE:  pSort[0] = 1;
    pSort[NumSort+1] = 2;
    for (i=1; i<=NumSort-2; i++)
      pSort[i] = -(i+2);
    pSort[NumSort-1] = 0;
    pSort[NumSort]   = 0;

  LTWO:  
    s = 0;
    t = NumSort+1;
    p = pSort[s];
    q = pSort[t];
    if ( q == 0 )
      goto SORTED;

 	LTHREE:  
 	
 		if (_ListCompareKeys((LPVOID)lpInArr[p-1], (LPVOID)lpInArr[q-1], pSortInfo) > 0)
      goto LSIX;

  LFOUR:  pSort[s] = sign(pSort[s],p);
    s = p;
    p = pSort[p];
    if ( p > 0 )
      goto LTHREE;

  LFIVE:  pSort[s] = q;
    s = t;
    do {       
      t = q;
      q = pSort[q];
    }
    while ( q > 0 );
    goto LEIGHT;

  LSIX:
    pSort[s] = sign(pSort[s],q);
    s = q;
    q = pSort[q];
    if ( q > 0 )
      goto LTHREE;

  LSEVEN:  pSort[s] = p;
    s = t;
    do {
      t = p;
      p = pSort[p];
    }
    while ( p > 0 );

  LEIGHT:  p = -p;
    q = -q;
    if ( q == 0 ) {
      pSort[s] = sign(pSort[s],p);
      pSort[t] = sign(pSort[t],0);
      goto LTWO;
    }
    goto LTHREE;

SORTED:    /* unwind "linked" pSort into gSort */

  for ( i=p=0; pSort[i] !=0; i=pSort[i], p++ ) 
    pOutArr[p] = pSort[i] - 1;
	
	GlobalUnlock(hMem);
	GlobalFree(hMem);

}  /* end SortgSort */


#define STAT_EYESONLY							0x00001
#define STAT_CONFIDENTIAL					0x00002
#define STAT_NEW									0x00004
#define STAT_URGENT								0x00008
#define STAT_REPLY								0x00008
#define STAT_CERTIFIED						0x00010

int _ListCompareKeys(LPVOID lpItem1, LPVOID lpItem2, LPLISTSORTINFO pInfo)
{
//	LPSTR pItem1; 
//	LPSTR pItem2;
	int i;                        
	LPTSTR ptr1;
	LPTSTR ptr2;
	BYTE FAR *lpbItem1;
	BYTE FAR *lpbItem2;
	DWORD dw1, dw2;
	int res;
//	char b1[20], b2[20];
	BOOL Same = TRUE;
	int i1, i2;      
	long l1, l2;             
	TCHAR null = '\0';
	
	lpbItem1 = (BYTE FAR *)lpItem1;
	lpbItem2 = (BYTE FAR *)lpItem2;
	if (lpbItem1 == NULL) return 0;
	if (lpbItem2 == NULL) return 0;

	for (i = 0; i < pInfo->NumKeys; i++)
	{                         
		switch (pInfo->Key[i].DataType)
		{
			case estString:
				ptr1 = (LPTSTR)(lpbItem1 + pInfo->Key[i].Offset);
				ptr2 = (LPTSTR)(lpbItem2 + pInfo->Key[i].Offset);
				res = STRICMP(ptr1, ptr2);
				break;
			case estLPSZ:
				ptr1 = (LPTSTR)*(DWORD FAR *)(lpbItem1 + pInfo->Key[i].Offset);
				ptr2 = (LPTSTR)*(DWORD FAR *)(lpbItem2 + pInfo->Key[i].Offset);
				if (ptr1 == NULL)
					ptr1 = (LPTSTR)&null;
				if (ptr2 == NULL)
					ptr2 = (LPTSTR)&null;
				res = STRICMP(ptr1, ptr2);
    		break;
    	case estInt:
  			i1 = *(LPINT)(lpbItem1 + pInfo->Key[i].Offset);
  			i2 = *(LPINT)(lpbItem2 + pInfo->Key[i].Offset);
    		res = (i1 == i2) ? 0 : ((i1 > i2) ? 1 : -1);
    		break;
    	case estDateStr:
				ptr1 = (LPTSTR)(lpbItem1 + pInfo->Key[i].Offset);
				ptr2 = (LPTSTR)(lpbItem2 + pInfo->Key[i].Offset);
    		l1 = _SwapDate(ptr1);
    		l2 = _SwapDate(ptr2);
    		res = (l1 == l2) ? 0 : ((l1 > l2) ? 1 : -1);
  			break;       
    	case estlpDateStr:
				ptr1 = (LPTSTR)*(DWORD FAR *)(lpbItem1 + pInfo->Key[i].Offset);
				ptr2 = (LPTSTR)*(DWORD FAR *)(lpbItem2 + pInfo->Key[i].Offset);
    		l1 = _SwapDate(ptr1);
    		l2 = _SwapDate(ptr2);
    		res = (l1 == l2) ? 0 : ((l1 > l2) ? 1 : -1);
    		break;
  		case estDateDword:
  			dw1 = *(DWORD FAR *)(lpbItem1 + pInfo->Key[i].Offset);
  			dw2 = *(DWORD FAR *)(lpbItem2 + pInfo->Key[i].Offset);
    		res = (dw1 == dw2) ? 0 : ((dw1 > dw2) ? 1 : -1);
    		break;
			case estdwStatus:
  			dw1 = *(DWORD FAR *)(lpbItem1 + pInfo->Key[i].Offset);
  			dw2 = *(DWORD FAR *)(lpbItem2 + pInfo->Key[i].Offset);
				res = 0;
				if (dw1 & STAT_NEW)
				{
					if (!(dw2 & STAT_NEW))
						res = 1;
				}
				else
				{
					if (dw2 & STAT_NEW)
						res = -1;
				}
				break;
    }
		if (res != 0) //if strings are not equal return result, otherwise compare next key
		{
			Same = FALSE;
			break;
		}
	}
	if (Same)  //if all keys are identical compare addressed
	{
		res = (lpItem1 > lpItem2) ? 1 : -1;
	}
	if (!pInfo->Ascending) res = (-res);
//	GlobalUnlock(item1);
//	GlobalUnlock(item2);
	return res;
}
		

long _SwapDate(LPTSTR date) //date in dd/mm/yy format
{
#ifdef UNDER_CE
	return 0L;
#else
	TCHAR buff[30];
	
	buff[0] = date[6];
	buff[1] = date[7];
	buff[2] = date[3];
	buff[3] = date[4];
	buff[4] = date[0];
	buff[5] = date[1];
	buff[6] = '\0';
	return(atol(buff));
#endif
}
	